package example;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayV2HTTPResponse;
import com.amazonaws.services.lambda.runtime.events.CloudFrontEvent;
import com.amazonaws.services.lambda.runtime.events.CloudWatchLogsEvent;
import com.amazonaws.services.lambda.runtime.events.CodeCommitEvent;
import com.amazonaws.services.lambda.runtime.events.CognitoEvent;
import com.amazonaws.services.lambda.runtime.events.ConfigEvent;
import com.amazonaws.services.lambda.runtime.events.DynamodbEvent;
import com.amazonaws.services.lambda.runtime.events.KinesisEvent;
import com.amazonaws.services.lambda.runtime.events.KinesisFirehoseEvent;
import com.amazonaws.services.lambda.runtime.events.LexEvent;
import com.amazonaws.services.lambda.runtime.events.S3Event;
import com.amazonaws.services.lambda.runtime.events.SNSEvent;
import com.amazonaws.services.lambda.runtime.events.SQSEvent;
import com.amazonaws.services.lambda.runtime.events.ScheduledEvent;
import com.amazonaws.services.lambda.runtime.tests.annotations.Event;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import java.util.List;

import org.junit.jupiter.params.ParameterizedTest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

class InvokeTest {

  private static final Logger logger = LoggerFactory.getLogger(InvokeTest.class);
  Gson gson = new GsonBuilder().setPrettyPrinting().create();

  @ParameterizedTest
  @Event(value = "events/apigateway-v1.json", type = APIGatewayProxyRequestEvent.class)
  void testApiGatewayV1(APIGatewayProxyRequestEvent event) {
    logger.info("Invoke TEST - ApiGatewayV1");
    Context context = new TestContext();
    HandlerApiGatewayV1 handler = new HandlerApiGatewayV1();
    APIGatewayProxyResponseEvent response = handler.handleRequest(event, context);
    String expected = "<!DOCTYPE html><html><head><title>" + "Hello world!" + "</title></head><body>" +
      "<h1>Welcome</h1><p>Page generated by a Lambda function.</p>" +
      "</body></html>";
    assertEquals(expected, response.getBody());
  }

  @ParameterizedTest
  @Event(value = "events/apigateway-v2.json", type = APIGatewayV2HTTPEvent.class)
  void testApiGatewayV2(APIGatewayV2HTTPEvent event) {
    logger.info("Invoke TEST - ApiGatewayV1");
    Context context = new TestContext();
    HandlerApiGatewayV2 handler = new HandlerApiGatewayV2();
    APIGatewayV2HTTPResponse response = handler.handleRequest(event, context);
    String expected = "<!DOCTYPE html><html><head><title>" + "Hello world!" + "</title></head><body>" +
      "<h1>Welcome</h1><p>Page generated by a Lambda function.</p>" +
      "</body></html>";
    assertEquals(expected, response.getBody());
  }

  @ParameterizedTest
  @Event(value = "events/cloudfront.json", type = CloudFrontEvent.class)
  void testCloudFront(CloudFrontEvent event) {
    logger.info("Invoke TEST - CloudFront");
    Context context = new TestContext();
    HandlerCloudFront handler = new HandlerCloudFront();
    List<String> response = handler.handleRequest(event, context);
    assertEquals(1, response.size());
    assertEquals("/picture.jpg", response.get(0));
  }

  @ParameterizedTest
  @Event(value = "events/codecommit-push.json", type = CodeCommitEvent.class)
  void testCodeCommit(CodeCommitEvent event) {
    logger.info("Invoke TEST - CodeCommit");
    Context context = new TestContext();
    HandlerCodeCommit handler = new HandlerCodeCommit();
    List<String> response = handler.handleRequest(event, context);
    assertEquals(1, response.size());
    assertEquals("5c4ef1049f1d27deadbeeff313e0730018be182b", response.get(0));
  }

  @ParameterizedTest
  @Event(value = "events/cognito-sync.json", type = CognitoEvent.class)
  void testCognito(CognitoEvent event) {
    logger.info("Invoke TEST - Cognito");
    Context context = new TestContext();
    HandlerCognito handler = new HandlerCognito();
    List<String> response = handler.handleRequest(event, context);
    assertEquals(2, response.size());
    assertEquals("replace", response.get(0));
    assertEquals("replace", response.get(1));
  }

  @ParameterizedTest
  @Event(value = "events/config-rule.json", type = ConfigEvent.class)
  void testConfig(ConfigEvent event) {
    logger.info("Invoke TEST - Config");
    Context context = new TestContext();
    HandlerConfig handler = new HandlerConfig();
    String response = handler.handleRequest(event, context);
    assertEquals("arn:aws:config:ca-central-1:123456789012:config-rule/config-rule-0123456", response);
  }

  @ParameterizedTest
  @Event(value = "events/cloudwatch-scheduled.json", type = ScheduledEvent.class)
  void testCWEvents(ScheduledEvent event) {
    logger.info("Invoke TEST - CWEvents");
    Context context = new TestContext();
    HandlerCWEvents handler = new HandlerCWEvents();
    List<String> response = handler.handleRequest(event, context);
    assertEquals(1, response.size());
    assertEquals("arn:aws:events:us-east-2:123456789012:rule/my-rule", response.get(0));
  }

  @ParameterizedTest
  @Event(value = "events/cloudwatch-logs.json", type = CloudWatchLogsEvent.class)
  void testCWLogs(CloudWatchLogsEvent event) {
    logger.info("Invoke TEST - CWLogs");
    Context context = new TestContext();
    HandlerCWLogs handler = new HandlerCWLogs();
    String response = handler.handleRequest(event, context);
    assertNotNull(response);
  }

  @ParameterizedTest
  @Event(value = "events/dynamodb-record.json", type = DynamodbEvent.class)
  void testDynamoDB(DynamodbEvent event) {
    logger.info("Invoke TEST - DynamoDB");
    Context context = new TestContext();
    HandlerDynamoDB handler = new HandlerDynamoDB();
    List<String> response = handler.handleRequest(event, context);
    assertEquals(2, response.size());
    assertEquals("INSERT", response.get(0));
    assertEquals("MODIFY", response.get(1));
  }

  @ParameterizedTest
  @Event(value = "events/firehose-record.json", type = KinesisFirehoseEvent.class)
  void testFirehose(KinesisFirehoseEvent event) {
    logger.info("Invoke TEST - Firehose");
    Context context = new TestContext();
    HandlerFirehose handler = new HandlerFirehose();
    List<String> response = handler.handleRequest(event, context);
    assertEquals(1, response.size());
    assertEquals("49546986683135544286507457936321625675700192471156785154", response.get(0));
  }

  @ParameterizedTest
  @Event(value = "events/kinesis-record.json", type = KinesisEvent.class)
  void testKinesis(KinesisEvent event) {
    logger.info("Invoke TEST - Kinesis");
    Context context = new TestContext();
    HandlerKinesis handler = new HandlerKinesis();
    List<String> response = handler.handleRequest(event, context);
    assertEquals(2, response.size());
  }

  @ParameterizedTest
  @Event(value = "events/lex-flowers.json", type = LexEvent.class)
  void testLex(LexEvent event) {
    logger.info("Invoke TEST - Lex");
    Context context = new TestContext();
    HandlerLex handler = new HandlerLex();
    String response = handler.handleRequest(event, context);
    assertEquals("OrderFlowers", response);
  }

  @ParameterizedTest
  @Event(value = "events/s3-notification.json", type = S3Event.class)
  void testS3(S3Event event) {
    logger.info("Invoke TEST - S3");
    Context context = new TestContext();
    HandlerS3 handler = new HandlerS3();
    String response = handler.handleRequest(event, context);
    assertEquals("BUCKET_NAME/inbound/sample-java-s3.png", response);
  }

  @ParameterizedTest
  @Event(value = "events/sns-notification.json", type = SNSEvent.class)
  void testSNS(SNSEvent event) {
    logger.info("Invoke TEST - SNS");
    Context context = new TestContext();
    HandlerSNS handler = new HandlerSNS();
    List<String> response = handler.handleRequest(event, context);
    assertEquals(1, response.size());
    assertEquals("Updated and expanded documentation for using Lambda with API Gateway, including support for HTTP APIs.", response.get(0));
  }

  @ParameterizedTest
  @Event(value = "events/sqs-record.json", type = SQSEvent.class)
  void testSQS(SQSEvent event) {
    logger.info("Invoke TEST - SQS");
    Context context = new TestContext();
    HandlerSQS handler = new HandlerSQS();
    List<String> response = handler.handleRequest(event, context);
    assertEquals(1, response.size());
    assertEquals("Hello from SQS!", response.get(0));
  }
}
